﻿/*
 * Copyright (c) 1995 The Regents of the University of California.
 * All rights reserved.    
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice and the following
 * two paragraphs appear in all copies of this software.
 *
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.    THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 */

/*
 * Portions of this software Copyright (c) 1995 Brown University.
 * All rights reserved.
 * 
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement
 * is hereby granted, provided that the above copyright notice and the
 * following two paragraphs appear in all copies of this software.
 * 
 * IN NO EVENT SHALL BROWN UNIVERSITY BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF BROWN
 * UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * BROWN UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE.    THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
 * BASIS, AND BROWN UNIVERSITY HAS NO OBLIGATION TO PROVIDE MAINTENANCE,
 * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 */

/*
Portions copyright (c) 2012 Eric Lasota

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

using System;
using System.Collections.Generic;
using System.Text;

namespace MPEGnet
{
    public partial class VidStream
    {
        
        /* DCT coeff tables. */

        const int RUN_MASK = 0xfc00;
        const int LEVEL_MASK = 0x03f0;
        const int NUM_MASK = 0x000f;
        const int RUN_SHIFT = 10;
        const int LEVEL_SHIFT = 4;

        void DecodeDCTCoeff(ushort[] dct_coeff_tbl, out uint run, ref int level)
        {
            uint temp, index;
            uint value, next32bits;
            int flushed;
            /*
             * Grab the next 32 bits and use it to improve performance of
             * getting the bits to parse. Thus, calls are translated as:
             *
             *	show_bitsX  <-->   next32bits >> (32-X)
             *	get_bitsX   <-->   val = next32bits >> (32-flushed-X);
             *			   flushed += X;
             *			   next32bits &= bitMask[flushed];
             *	flush_bitsX <-->   flushed += X;
             *			   next32bits &= bitMask[flushed];
             *
             * I've streamlined the code a lot, so that we don't have to mask
             * out the low order bits and a few of the extra adds are removed.
             */
            next32bits = show_bits32();
            /* show_bits8(index); */
            index = next32bits >> 24;
            if (index > 3)
            {
                value = dct_coeff_tbl[index];
                run = value >> RUN_SHIFT;
                if (run != VlcTables.END_OF_BLOCK)
                {
                    /* num_bits = (value & NUM_MASK) + 1; */
                    /* flush_bits(num_bits); */
                    if (run != VlcTables.ESCAPE)
                    {
	                    /* get_bits1(value); */
	                    /* if (value) level = -level; */
	                    flushed = (int)((value & NUM_MASK) + 2);
                        level = (int)((value & LEVEL_MASK) >> LEVEL_SHIFT);
	                    value = next32bits >> (32-flushed);
	                    value &= 0x1;
	                    if (value != 0)
                            level = -level;
	                    /* next32bits &= ((~0) >> flushed);  last op before update */

                        /* Update bitstream... */
                        flush_bits(flushed);
                    }
                    else
                    {
                        /* run == ESCAPE */
	                    /* Get the next six into run, and next 8 into temp */
                        /* get_bits14(temp); */
	                    flushed = (int)((value & NUM_MASK) + 1);
	                    temp = next32bits >> (18-flushed);
	                    /* Normally, we'd ad 14 to flushed, but I've saved a few
	                     * instr by moving the add below */
	                    temp &= 0x3fff;
                        run = temp >> 8;
                        temp &= 0xff;
	                    if (temp == 0)
                        {
                            /* get_bits8(level); */
	                        level = (int)(next32bits >> (10-flushed));
	                        level &= 0xff;
	                        flushed += 22;
 	                        /* CG: 12jul2000 - assert(level >= 128); */
 	                        if (level >= 128)
                            {
                                /* Update bitstream... */
                                flush_bits(flushed);
	                        }
                            else
                            {
	                            run = VlcTables.END_OF_BLOCK;
	                            level = VlcTables.END_OF_BLOCK;
	                        }
	                    }
                        else if (temp != 128)
                        {
                            /* Grab sign bit */
	                        flushed += 14;
	                        level = ((int) (temp << 24)) >> 24;
                            /* Update bitstream... */
                            flush_bits(flushed);
	                    }
                        else
                        {
                            /* get_bits8(level); */
	                        level = (int)(next32bits >> (10-flushed));
	                        level &= 0xff;
	                        flushed += 22;
	                        level = level - 256;
	                        /* CG: 12jul2000 - assert(level <= -128 && level >= -255); */
	                        if ( level <= -128 && level >= -255)
                            {
                                /* Update bitstream... */
                                flush_bits(flushed);
                    	    }
                            else
                            {
	                            run = VlcTables.END_OF_BLOCK;
	                            level = VlcTables.END_OF_BLOCK;
	                        }
	                    }
                    }
                }
            }
            else
            {
                switch (index)
                {
                    case 2:
                        {
                            /* show_bits10(index); */
                            index = next32bits >> 22;
                            value = VlcTables.dct_coeff_tbl_2[index & 3];
                            break;
                        }
                    case 3:
                        {
                            /* show_bits10(index); */
                            index = next32bits >> 22;
                            value = VlcTables.dct_coeff_tbl_3[index & 3];
                            break;
                        }
                    case 1:
                        {
                            /* show_bits12(index); */
                            index = next32bits >> 20;
                            value = VlcTables.dct_coeff_tbl_1[index & 15];
                            break;
                        }
                    default:
                        {
                            /* index == 0 */
                            /* show_bits16(index); */
                            index = next32bits >> 16;
                            value = VlcTables.dct_coeff_tbl_0[index & 255];
                            break;
                        }
                }
                run = value >> RUN_SHIFT;
                level = (int)((value & LEVEL_MASK) >> LEVEL_SHIFT);

                /*
                 * Fold these operations together to make it fast...
                 */
                /* num_bits = (value & NUM_MASK) + 1; */
                /* flush_bits(num_bits); */
                /* get_bits1(value); */
                /* if (value) level = -level; */

                flushed = (int)((value & NUM_MASK) + 2);
                value = next32bits >> (32-flushed);
                value &= 0x1;
                if (value != 0)
                    level = -level;

                /* Update bitstream ... */
                flush_bits(flushed);
            }
        }
        
        void DecodeDCTCoeffFirst(out uint runval, ref int levelval)
        {
            DecodeDCTCoeff(VlcTables.dct_coeff_first, out runval, ref levelval);
        }

        void DecodeDCTCoeffNext(out uint runval, ref int levelval)
        {
            DecodeDCTCoeff(VlcTables.dct_coeff_next, out runval, ref levelval);
        }

        uint DecodeDCTDCSizeLum()
        {
            uint index = show_bits5();
            if (index < 31)
            {
  	            flush_bits(VlcTables.dct_dc_size_luminance[index].num_bits);
  	            return VlcTables.dct_dc_size_luminance[index].value;
            }
            else
            {
	            index = show_bits9();
	            index -= 0x1f0;
	            flush_bits(VlcTables.dct_dc_size_luminance1[index].num_bits);
	            return VlcTables.dct_dc_size_luminance1[index].value;
            }
        }

        uint DecodeDCTDCSizeChrom()
        {
            uint index = show_bits5();

            if (index < 31)
            {
                flush_bits(VlcTables.dct_dc_size_chrominance[index].num_bits);
                return VlcTables.dct_dc_size_chrominance[index].value;
  	        }
            else
            {
                index = show_bits10();
                index -= 0x3e0;
	            flush_bits(VlcTables.dct_dc_size_chrominance1[index].num_bits);
                return VlcTables.dct_dc_size_chrominance1[index].value;
            }
        }
    }
}
